/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 */
package org.olap4j.transform;

import java.util.ArrayList;
import java.util.List;

/**
 * Generic Tree Node. Adapted from JPivot (class com.tonbeller.jpivot.util.TreeNode)
 * 
 * <p>
 * REVIEW: Should this class be in the public olap4j API? (jhyde, 2008/8/14)
 * 
 * @author etdub
 * @since Aug 7, 2008
 */
class TreeNode<T> {

	private TreeNode<T> parent = null;
	private final List<TreeNode<T>> children;
	private T reference;

	/**
	 * Constructor.
	 * 
	 * @param data
	 *            the reference to hold in the node
	 */
	public TreeNode(T data) {
		this.reference = data;
		this.children = new ArrayList<TreeNode<T>>();
	}

	/**
	 * Removes this node from the tree
	 */
	public void remove() {
		if (parent != null) {
			parent.removeChild(this);
		}
	}

	/**
	 * Removes child node from the tree, if it exists
	 * 
	 * @param child
	 *            node to remove
	 */
	public void removeChild(TreeNode<T> child) {
		if (children.contains(child)) {
			children.remove(child);
		}
	}

	/**
	 * Adds a child node to the tree
	 * 
	 * @param child
	 *            node to be added
	 */
	public void addChildNode(TreeNode<T> child) {
		child.parent = this;
		if (!children.contains(child)) {
			children.add(child);
		}
	}

	/**
	 * Performs a deep copy (clone) of this node The contained reference is not cloned but passed to the new node.
	 * 
	 * @return the cloned TreeNode
	 */
	public TreeNode<T> deepCopy() {
		TreeNode<T> newNode = new TreeNode<T>(reference);
		for (TreeNode<T> child : children) {
			newNode.addChildNode(child.deepCopy());
		}
		return newNode;
	}

	/**
	 * Performs a deep copy (clone) of this node, pruning all nodes below level specified by depth
	 * 
	 * @param depth
	 *            number of child levels to be copied
	 * @return the cloned TreeNode
	 */
	public TreeNode<T> deepCopyPrune(int depth) {
		if (depth < 0) {
			throw new IllegalArgumentException("Depth is negative");
		}
		TreeNode<T> newNode = new TreeNode<T>(reference);
		if (depth == 0) {
			return newNode;
		}
		for (TreeNode<T> child : children) {
			newNode.addChildNode(child.deepCopyPrune(depth - 1));
		}
		return newNode;
	}

	/**
	 * Returns the level of this node, i.e. distance from the root node
	 * 
	 * @return level distance from root node
	 */
	public int getLevel() {
		int level = 0;
		TreeNode<T> p = parent;
		while (p != null) {
			++level;
			p = p.parent;
		}
		return level;
	}

	/**
	 * Gets a list of children nodes.
	 * 
	 * <p>
	 * The list is mutable but shouldn't be modified by callers (use the add and remove methods instead).
	 * 
	 * @return the list of children
	 */
	public List<TreeNode<T>> getChildren() {
		return children;
	}

	/**
	 * Gets the parent node.
	 * 
	 * @return parent node
	 */
	public TreeNode<T> getParent() {
		return parent;
	}

	/**
	 * Get the contained reference object
	 * 
	 * @return the reference object
	 */
	public T getReference() {
		return reference;
	}

	/**
	 * Set the contained reference object
	 * 
	 * @param ref
	 *            the new reference object
	 */
	public void setReference(T ref) {
		this.reference = ref;
	}

	/**
	 * Walk through subtree of this node
	 * 
	 * @param callbackHandler
	 *            callback function called on iteration
	 * @return code used for navigation in the tree (@see TreeNodeCallback)
	 */
	public int walkTree(TreeNodeCallback<T> callbackHandler) {
		int code = callbackHandler.handleTreeNode(this);
		if (code != TreeNodeCallback.CONTINUE) {
			return code;
		}
		for (TreeNode<T> child : children) {
			code = child.walkTree(callbackHandler);
			if (code >= TreeNodeCallback.CONTINUE_PARENT) {
				return code;
			}
		}
		return code;
	}

	/**
	 * Walk through children subtrees of this node
	 * 
	 * @param callbackHandler
	 *            callback function called on iteration
	 * @return code used for navigation in the tree (@see TreeNodeCallback)
	 */
	public int walkChildren(TreeNodeCallback<T> callbackHandler) {
		int code = 0;
		for (TreeNode<T> child : children) {
			code = callbackHandler.handleTreeNode(child);
			if (code >= TreeNodeCallback.CONTINUE_PARENT) {
				return code;
			}
			if (code == TreeNodeCallback.CONTINUE) {
				code = child.walkChildren(callbackHandler);
				if (code > TreeNodeCallback.CONTINUE_PARENT) {
					return code;
				}
			}
		}
		return code;
	}

}

// End TreeNode.java
